﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Spi;
using NGuice.Inject.Internal;

namespace NGuice.Inject
{
    /// <summary>
    /// 装入的scope实现
    /// </summary>
    public class Scopes
    {
        private Scopes() { }

        private static readonly object NULL = new Object();

        /// <summary>
        /// 单例范围，每一个注入器一个实例
        /// </summary>
        public static readonly IScope SINGLETON = new SingletonScope();

        /// <summary>
        /// 无范围常数。注入器每次都包含一个无范围对象实例，他注入此对象并很快
        /// 就不保持此对象，下一次请求将获取一个新的对象
        /// </summary>
        public static readonly IScope NO_SCOPE = new NoScope();

        /// <summary>
        /// 如果binding为单例范围，则返回true。如果binding为LinkedKeyBinding并属于一个注入器（例如可以通过
        /// <code>Injector.GetBinding</code>获取，则如果目标binding为单例范围则此方法也将返回true
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="binding"></param>
        /// <returns></returns>
        public static bool IsSingleton<T>(IBinding binding)
        {
            do
            {
                bool singleton = binding.AcceptScopingVisitor(new BindingScopingVisitor());
                if (singleton)
                {
                    return true;
                }
                if (binding is LinkedBindingImpl<T>)
                {
                    LinkedBindingImpl<T> linkedBinding = (LinkedBindingImpl<T>)binding;
                    Injector injector = (Injector)linkedBinding.Injector;
                    if (injector != null)
                    {
                        binding = injector.GetBinding(linkedBinding.LinkedKey);
                        continue;
                    }
                }
                else if (binding is IExposedBinding<T>)
                {
                    IExposedBinding<T> exposedBinding = (IExposedBinding<T>)binding;
                    Injector injector = exposedBinding.PrivateElements.Injector;
                    if (injector != null)
                    {
                        binding = injector.GetBinding(exposedBinding.Key);
                        continue;
                    }
                }
                return false;
            } while (true);
        }

        internal  class SingletonScope : IScope
        {
            public IProvider<T> Scope<T>(Key<T> key, IProvider<T> creator)
            {
                return new SingletonProvider<T>(creator);
            }
        }

        internal class SingletonProvider<T> : IProvider<T>
        {
            /// <summary>
            /// 延迟初始化单例实例。一旦设置，他将具有type T或将等于NULL
            /// </summary>
            private volatile object instance;

            private IProvider<T> creator;

            public SingletonProvider(IProvider<T> creator)
            {
                this.creator = creator;
            }

            public T Get()
            {
                if (instance == null)
                {
                    lock (typeof(InternalInjectorCreator))
                    {
                        if (instance == null)
                        {
                            T provided = creator.Get();

                            // don't remember proxies; these exist only to serve circular dependencies
                            if (provided is ICircularDependencyProxy)
                            {
                                return provided;
                            }
                            object providedOrSentinel = (provided == null) ? NULL : provided;
                            if (instance != null && instance != providedOrSentinel)
                            {
                                throw new ProvisionException(
                                    "Provider was reentrant while creating a singleton");
                            }
                            instance = providedOrSentinel;
                        }
                    }
                }
                object localInstance = instance;
                // This is safe because instance has type T or is equal to NULL
                T returnedInstance = (localInstance != Scopes.NULL) ? (T)localInstance : default(T);
                return returnedInstance;
            }

            public override string ToString()
            {
                return string.Format("{0}[{1}]", creator, SINGLETON);
            }

            object IProvider.Get()
            {
                return Get();
            }
        }

        internal class NoScope : IScope
        {
            public IProvider<T> Scope<T>(Key<T> key, IProvider<T> unscoped)
            {
                return unscoped;
            }

            public override string ToString()
            {
                return "Scopes.NO_SCOPE";
             }
        }

        internal class BindingScopingVisitor: IBindingScopingVisitor<bool>
        {
             public bool VisitNoScoping()
             {
                return false;
             }

            public bool VisitScopeAnnotation(Type scopeAnnotation) {
              return scopeAnnotation == typeof(SingletonAttribute);
            }

            public bool VisitScope(IScope scope)
            {
              return scope == Scopes.SINGLETON;
            }

            public bool VisitEagerSingleton()
            {
              return true;
            }
        }
    }

    





}
